package com.feiyutech.camera2demo

import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.graphics.ImageFormat
import android.graphics.Matrix
import android.graphics.RectF
import android.graphics.SurfaceTexture
import android.hardware.SensorManager
import android.hardware.camera2.*
import android.media.ImageReader
import android.media.MediaActionSound
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.support.v7.app.AppCompatActivity
import android.util.Size
import android.view.OrientationEventListener
import android.view.Surface
import android.view.TextureView
import cn.zfs.common.utils.ToastUtils
import java.io.File
import java.util.*

/**
 * 描述:
 * 时间: 2018/8/4 22:27
 * 作者: zengfansheng
 */
abstract class BaseCameraActivity : AppCompatActivity() {
    var cameraManager: CameraManager? = null
    var imageReader: ImageReader? = null
    var cameraCaptureSession: CameraCaptureSession? = null
    var cameraDevice: CameraDevice? = null
    var cameraId = "0"
    var handleThread: HandlerThread? = null
    var backgroundHandler: Handler? = null
    var previewSize: Size? = null
    var largestSize: Size? = null
    var cameraCharacteristics: CameraCharacteristics? = null
    var preview: AutoFitTextureView? = null
    var surface: Surface? = null
    var outputSurfaces: MutableList<Surface>? = null
    var previewRequestBuilder: CaptureRequest.Builder? = null
    private var mediaActionSound: MediaActionSound? = null
    private var orientationEventListener: OrientationEventListener? = null
    var orientation = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mediaActionSound = MediaActionSound()
        mediaActionSound!!.load(MediaActionSound.SHUTTER_CLICK)
        orientationEventListener = object : OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
            override fun onOrientationChanged(orientation: Int) {
                if (orientation != OrientationEventListener.ORIENTATION_UNKNOWN) {
                    if (orientation >= 315 || orientation < 45) run {
                        this@BaseCameraActivity.orientation = 0
                    } else if (orientation in 45..134) run {
                        this@BaseCameraActivity.orientation = 90
                    } else if (orientation in 134..224) run {
                        this@BaseCameraActivity.orientation = 180
                    } else if (orientation in 224..314) run {
                        this@BaseCameraActivity.orientation = 270
                    }
                }
            }
        }
    }
    
    abstract fun getTextureView(): AutoFitTextureView
    
    abstract fun updateCaptrueRequestBuilder(builder: CaptureRequest.Builder)
    
    abstract fun getPictureSaveResultListener(): JpegReaderListener.OnSaveResultListener?

    private fun initTextureView(textureView: AutoFitTextureView) {
        this.preview = textureView
        textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
            override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {
                configureTransform(width, height)
            }

            override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {

            }

            override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean {
                return true
            }

            override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
                openCamera(width, height)
            }
        }
    }

    @SuppressLint("MissingPermission")
    fun openCamera(width: Int, height: Int) {
        handleThread = HandlerThread("Camera2")
        handleThread?.start()
        backgroundHandler = Handler(handleThread?.looper)
        try {
            cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager?
            setUpCameraOutputs(width, height)
            configureTransform(width, height)
            initOutputSurface()            
            //打开相机
            cameraManager!!.openCamera(cameraId, cameraDevicetateCallback, backgroundHandler)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    //得到CameraCharacteristics等信息，设置显示大小
    private fun setUpCameraOutputs(width: Int, height: Int) {
        cameraCharacteristics = null
        cameraManager?.cameraIdList?.forEach {
            val ch = cameraManager?.getCameraCharacteristics(it)
            if (ch!!.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)) {
                cameraCharacteristics = ch
                cameraId = it
                return@forEach
            }
        }
        if (cameraCharacteristics == null) {
            cameraCharacteristics = cameraManager?.getCameraCharacteristics(cameraId)
        }
        //流配置
        val map = cameraCharacteristics!!.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
        if (map != null) {
            largestSize = Collections.max(Arrays.asList(*map.getOutputSizes(ImageFormat.JPEG)), CompareSizesByArea())
            previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture::class.java), width, height, largestSize!!)
            val orientation = resources.configuration.orientation
            if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                preview?.setAspectRatio(previewSize!!.width, previewSize!!.height)
            } else {
                preview?.setAspectRatio(previewSize!!.height, previewSize!!.width)
            }
        }
    }

    private fun chooseOptimalSize(choices: Array<Size>, width: Int, height: Int, aspectRatio: Size): Size {
        // Collect the supported resolutions that are at least as big as the preview Surface
        val bigEnough = ArrayList<Size>()
        val w = aspectRatio.width
        val h = aspectRatio.height
        for (option in choices) {
            if (option.height == option.width * h / w && option.width >= width && option.height >= height) {
                bigEnough.add(option)
            }
        }

        // Pick the smallest of those, assuming we found any
        return if (bigEnough.size > 0) {
            Collections.min(bigEnough, CompareSizesByArea())
        } else {
            choices[0]
        }
    }

    private inner class CompareSizesByArea : Comparator<Size> {

        override fun compare(lhs: Size, rhs: Size): Int {
            return java.lang.Long.signum(lhs.width.toLong() * lhs.height - rhs.width.toLong() * rhs.height)
        }
    }

    /**
     * 官方sample中的
     */
    private fun configureTransform(viewWidth: Int, viewHeight: Int) {
        if (null == preview || null == previewSize) {
            return
        }
        val rotation = when (orientation) {
            0 -> Surface.ROTATION_0
            90 -> Surface.ROTATION_90
            270 -> Surface.ROTATION_270
            else -> Surface.ROTATION_180
        }
        val matrix = Matrix()
        val viewRect = RectF(0f, 0f, viewWidth.toFloat(), viewHeight.toFloat())
        val bufferRect = RectF(0f, 0f, previewSize!!.height.toFloat(), previewSize!!.width.toFloat())
        val centerX = viewRect.centerX()
        val centerY = viewRect.centerY()
        if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
            bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY())
            matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL)
            val scale = Math.max(viewHeight.toFloat() / previewSize!!.height, viewWidth.toFloat() / previewSize!!.width)
            matrix.postScale(scale, scale, centerX, centerY)
            matrix.postRotate((90 * (rotation - 2)).toFloat(), centerX, centerY)
        }
        preview!!.setTransform(matrix)
    }

    private fun initOutputSurface() {
        //得到ImageReader对象,5为maxImage，放入队列里面的最大连拍张数(应该是这个意思)
        imageReader = ImageReader.newInstance(largestSize!!.width, largestSize!!.height, ImageFormat.JPEG, 1)
        imageReader!!.setOnImageAvailableListener(JpegReaderListener(object : JpegReaderListener.OnSaveResultListener {
            override fun onSaveResult(file: File?) {
                mediaActionSound?.play(MediaActionSound.SHUTTER_CLICK)
                getPictureSaveResultListener()?.onSaveResult(file)
            }
        }), backgroundHandler)
        //得到SurfaceTexture
        val texture = preview!!.surfaceTexture
        //设置默认的图像缓冲区的大小
        texture.setDefaultBufferSize(previewSize!!.width, previewSize!!.height)
        //显示的Surface
        surface = Surface(texture)
        //得到surface
        outputSurfaces = ArrayList(2)
        outputSurfaces!!.add(imageReader!!.surface)
        outputSurfaces!!.add(surface!!)
    }

    fun closeCamera() {
        //关闭相机
        cameraDevice?.close()
        cameraDevice = null
        handleThread?.quit()
        handleThread = null
        backgroundHandler = null
    }

    override fun onStop() {
        super.onStop()
        orientationEventListener?.disable()
        closeCamera()
    }

    override fun onStart() {
        super.onStart()
        orientationEventListener?.enable()
        if (preview != null && preview!!.isAvailable) {
            openCamera(preview!!.width, preview!!.height)
        } else {
            initTextureView(getTextureView())
        }
    }

    private val cameraDevicetateCallback = object : CameraDevice.StateCallback() {
        override fun onOpened(camera: CameraDevice?) {
            cameraDevice = camera
            // 开启预览
            startPreview()
        }

        override fun onDisconnected(camera: CameraDevice?) {
            cameraDevice?.close()
            cameraDevice = null
        }

        override fun onError(camera: CameraDevice?, error: Int) {
            ToastUtils.showShort("摄像头开启失败: $error")
        }
    }

    private val captureSessionStateCallback = object : CameraCaptureSession.StateCallback() {
        override fun onConfigureFailed(session: CameraCaptureSession?) {
            ToastUtils.showShort("CameraCaptureSession配置失败")
        }

        override fun onConfigured(session: CameraCaptureSession?) {
            cameraCaptureSession = session
            updatePreview()
        }
    }

    private fun startPreview() {
        try {
            previewRequestBuilder = cameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
            // 将surface作为CaptureRequest.Builder的目标
            previewRequestBuilder?.addTarget(surface)
            // 自动对焦
            previewRequestBuilder!!.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
            cameraDevice?.createCaptureSession(outputSurfaces, captureSessionStateCallback, backgroundHandler)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    fun takeCapture() {
        try {
            val captureRequestBuilder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
            // 将imageReader的surface作为CaptureRequest.Builder的目标
            captureRequestBuilder.addTarget(imageReader!!.surface)
            updateCaptrueRequestBuilder(captureRequestBuilder)
            // 根据设备方向计算设置照片的方向
            val orien = when (orientation) {
                0 -> 90
                90 -> 180
                270 -> 0
                else -> 270
            }
            captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, orien)
            //CameraCaptureSession调用captuer开始拍照，调用setRepeatingRequest进行预览
            cameraCaptureSession!!.capture(captureRequestBuilder.build(), null, backgroundHandler)            
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
    
    fun updatePreview() {
        try {
            cameraCaptureSession?.setRepeatingRequest(previewRequestBuilder?.build(), null, backgroundHandler)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}